#include <netLib/server.h>

#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef _WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
#  include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif

NetServer *NetServer::pThis;
void listener_cb(struct evconnlistener *, evutil_socket_t,struct sockaddr *, int socklen, void *);
void conn_writecb(struct bufferevent *, void *);
void conn_eventcb(struct bufferevent *, short, void *);
void readServerCb(struct bufferevent *bufevt, void *ctx);
void signal_cb(evutil_socket_t, short, void *);
//cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=DEBUG ..
NetServer::NetServer()
{
    pThis = this;
    //printf("%p %#X\n",pThis,reinterpret_cast<int>( pThis));
#ifdef WIN32
    WORD wdVersion = MAKEWORD(2, 2);//定义自己需要的网络库版本，这里是2.2
	WSADATA wdSockMsg;//这是一个结构体
	int nRes = WSAStartup(wdVersion, &wdSockMsg);//打开一个套接字
#endif
}

void NetServer::setReadCb(RecvFun *pReadCb)
{
    m_pRecvFun = pReadCb;
}

bool NetServer::init(int port)
{
	struct event *signal_event;
	struct sockaddr_in sin = {0};

	m_pBase = event_base_new();
	if (!m_pBase) {
		fprintf(stderr, "Could not initialize libevent!\n");
		return 1;
	}

	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);

	m_pListener = evconnlistener_new_bind(m_pBase, listener_cb, (void *)m_pBase,
	    LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
	    (struct sockaddr*)&sin,
	    sizeof(sin));

	if (!m_pListener) {
		fprintf(stderr, "Could not create a listener!\n");
		return false;
	}

	signal_event = evsignal_new(m_pBase, SIGINT, signal_cb, (void *)m_pBase);

	if (!signal_event || event_add(signal_event, NULL)<0) {
		fprintf(stderr, "Could not create/add a signal event!\n");
		return false;
	}

	printf("done\n");
    
    return true;
}

void NetServer::loop()
{
    event_base_dispatch(m_pBase);
}

void NetServer::disconnect()
{
    evconnlistener_free(m_pListener);
	//event_free(signal_event);
	event_base_free(m_pBase);
}

void NetServer::onListen(struct evconnlistener *, 
    evutil_socket_t fd,struct sockaddr *, int socklen, void *user_data)
{
    struct event_base *m_pBase = (event_base*)user_data;
	struct bufferevent *bev;

	bev = bufferevent_socket_new(m_pBase, fd, BEV_OPT_CLOSE_ON_FREE);
	if (!bev) {
		fprintf(stderr, "Error constructing bufferevent!");
		event_base_loopbreak(m_pBase);
		return;
	}
    
	bufferevent_setcb(bev, readServerCb, conn_writecb, conn_eventcb, NULL);
	bufferevent_enable(bev, EV_WRITE|EV_READ);
	//bufferevent_disable(bev, EV_READ);
}

void NetServer::onRecv(struct bufferevent *bufevt, void *ctx)
{
    auto input = bufferevent_get_input(bufevt);
	int len = evbuffer_get_length(input);
	if( m_recvBuf.size()<len)
	{
		m_recvBuf.resize(len);
	}
    size_t sz = bufferevent_read(bufevt, m_recvBuf.data(), len);
    printf("received bytes: %d\n", sz);
    
    if( m_pRecvFun )
    {
        (*m_pRecvFun)(0,m_recvBuf.data(),sz);
    }
}

void log_callback(int severity, const char *msg)
{
	char szBuffer[512];
 
	FILE *pFd = fopen("./log.txt", "ab+");
	if (pFd == NULL)
		return;
 
	const char *severity_str;
		switch (severity) 
		{
			case EVENT_LOG_DEBUG:
				severity_str = "debug";
				break;
			case EVENT_LOG_MSG:
				severity_str = "msg";
				break;
			case EVENT_LOG_WARN:
				severity_str = "warn";
				break;
			case EVENT_LOG_ERR:
				severity_str = "err";
				break;
			default:
				severity_str = "???";
				break;
		}
	
	snprintf(szBuffer, sizeof(szBuffer), "[%s]:%s", severity_str, msg);
 
	(void)fwrite(szBuffer, 1, strlen(szBuffer), pFd);
 
	fclose(pFd);
}

void readServerCb(struct bufferevent *bufevt, void *ctx)
{
    NetServer::pThis->onRecv(bufevt,ctx);
}

void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    struct sockaddr *sa, int socklen, void *user_data)
{
	NetServer::pThis->onListen(listener,fd,sa,socklen,user_data);
}

void conn_writecb(struct bufferevent *bev, void *user_data)
{
	struct evbuffer *output = bufferevent_get_output(bev);
	if (evbuffer_get_length(output) == 0) {
        //bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
		//printf("flushed answer\n");
		//bufferevent_free(bev);
	}
}

void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
	if (events & BEV_EVENT_EOF) {
		printf("Connection closed.\n");
	} else if (events & BEV_EVENT_ERROR) {
		printf("Got an error on the connection: %s\n",
		    strerror(errno));/*XXX win32*/
	}
	/* None of the other events can happen here, since we haven't enabled
	 * timeouts */
	bufferevent_free(bev);
}

void signal_cb(evutil_socket_t sig, short events, void *user_data)
{
	struct event_base *base = (event_base*)user_data;
	struct timeval delay = { 2, 0 };

	printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");

	event_base_loopexit(base, &delay);
}
